package lion.dev.web.useragent;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;

import lion.dev.lang.KV;

/**
 *
 *
 *
 * @author lion
 * @email hl_0074@sina.com
 * @date 2014年5月30日
 */
public class UserAgentParser {

	static Map<String, IUAParser> visiablesMap = new HashMap<>();
	private static boolean inited;

	public static void init() {

		if (!inited) {
			synchronized (visiablesMap) {
				if (!inited) {
					loadVisiables();
					inited = true;
				}
			}
		}
	}

	private static void loadVisiables() {

		List<String> lines = null;
		try {
			lines = IOUtils.readLines(UserAgent.class.getResourceAsStream("/META-INF/ua.txt"));
		} catch (IOException e1) {
			lines = new ArrayList<>();
		}
		for (String line : lines) {
			if (StringUtils.isBlank(line) || line.startsWith("#")) {
				continue;
			}
			String[] keys = line.split(",");
			String ty = keys[0];
			String key = keys[1];
			String desc = null;
			if (keys.length > 2) {
				desc = keys[2];
			}
			visiablesMap.put(key, VisiableFactory.create(ty, desc));
		}
	}

	public static UserAgent parse(String agentStr) {

		init();

		UserAgent agent = new UserAgent(agentStr);
		UserAgentToken uatoken = new UserAgentToken(agentStr);
		String tmp = null;

		while ((tmp = uatoken.next()) != null) {
			tmp = tmp.trim();
			if (StringUtils.isBlank(tmp)) {
				continue;
			}
			if (!parseAgentToken(tmp, agent)) {
				if (uatoken.isAloneToken()) {
					continue;
				}
				uatoken.mark();
				boolean b = false;
				for (int i = 0; i < 2 && !b; i++) {
					String x = uatoken.next();
					if (x == null) {
						break;
					}
					tmp += " " + x;
					b = parseAgentToken(tmp, agent);
				}
				if (!b) {
					uatoken.reset();
				}
			}
		}
		// s
		return agent;
	}

	private static boolean parseAgentToken(String token, UserAgent agent) {

		IUAParser s = getVisiable(token);
		if (s != null) {
			s.visit(token, agent);
			return true;
		}
		// "/" split
		if (token.indexOf('/') > 0) {
			if (token.indexOf('/') == token.lastIndexOf('/')) {
				parseVersionToken(token, agent);
			} else {

				String t = token.substring(0, token.lastIndexOf("/"));
				int idx = t.lastIndexOf("/");

				String[] a = StringUtils.split(token.substring(0, idx), "/");
				for (String x : a) {
					IUAParser iUAParser = getVisiable(x);
					if (iUAParser != null) {
						iUAParser.visit(x, agent);
					}
				}
				String versionToken = token.substring(idx + 1);
				parseVersionToken(versionToken, agent);
			}
		}
		return false;
	}

	private static void parseVersionToken(String token, UserAgent userAgent) {

		if (token.indexOf("/") == -1) { return; }
		IUAParser iUAParser = getVisiable(token.substring(0, token.indexOf("/")));
		if (iUAParser != null) {
			iUAParser.visit(token, userAgent);
		}
	}

	public static List<KV<String, String>> explain(String userAgent) {

		init();
		UserAgentToken uatoken = new UserAgentToken(userAgent);
		String tmp = null;
		List<KV<String, String>> result = new ArrayList<>();
		while ((tmp = uatoken.next()) != null) {
			tmp = tmp.trim();
			List<KV<String, String>> s = explainToken(tmp);
			if (s.isEmpty()) {
				if (uatoken.isAloneToken()) {
					result.add(new KV<>(tmp, "unknow"));
					continue;
				}
				uatoken.mark();
				String str = tmp;
				for (int i = 0; i < 2 && s.isEmpty(); i++) {
					String x = uatoken.next();
					if (x == null) {
						break;
					}
					str += " " + x;
					s = explainToken(str);
				}
				if (s.isEmpty()) {
					uatoken.reset();
				}
			}
			if (!s.isEmpty()) {
				result.addAll(s);
			} else {
				result.add(new KV<>(tmp, "unknow"));
			}
		}
		return result;
	}

	private static List<KV<String, String>> explainToken(String token) {

		IUAParser iUAParser = getVisiable(token);

		if (iUAParser != null) { return iUAParser.explain(token); }
		// "/" split
		List<KV<String, String>> result = new ArrayList<>();
		if (token.indexOf('/') > 0) {
			if (token.indexOf('/') == token.lastIndexOf('/')) {
				explainVersion(token, result);
			} else if (token.indexOf('/') > 0 && token.indexOf('/') != token.lastIndexOf('/')) {
				String t = token.substring(0, token.lastIndexOf("/"));
				int idx = t.lastIndexOf("/");
				String[] tokens = token.substring(0, idx).split("/");

				for (String tk : tokens) {
					IUAParser tmp = getVisiable(tk);
					if (tmp != null) {
						result.addAll(tmp.explain(tk));
					}
				}
				String versionToken = token.substring(idx + 1);
				explainVersion(versionToken, result);
			}
		}
		return result;
	}

	private static IUAParser getVisiable(String token) {

		IUAParser v = visiablesMap.get(token);
		if (v == null && token.indexOf("/") < 0) {
			for (int i = 0; i < 2; i++) {
				if (token.indexOf(" ") > 0) {
					token = token.substring(0, token.lastIndexOf(" "));
					v = visiablesMap.get(token);
				}
				if (v != null) {
					break;
				}
			}
		}

		return v;
	}

	private static void explainVersion(String token, List<KV<String, String>> result) {

		if (token.indexOf("/") == -1) { return; }

		String[] tokens = token.split("/");
		IUAParser iUAParser = getVisiable(tokens[0]);
		if (iUAParser != null) {
			result.addAll(iUAParser.explain(token));
		}
		if (tokens.length > 1) {
			iUAParser = getVisiable(tokens[1]);
			if (iUAParser != null) {
				result.addAll(iUAParser.explain(tokens[1]));
			}
		}
	}
}
